package com.chengqs.dfs.backupnode.server;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * <B>主类名称：</B>FSNamesystem<BR>
 * <B>概要说明：</B>负责管理元数据的核心组件<BR>
 *
 * @author: chengqs
 * @create: 2025-03-30 03:48:59
 **/
@Slf4j
public class FSNameSystem {
    /**
     * 负责管理内存文件目录树的组件
     */
    private FSDirectory directory;

    private long checkpointTime;

    private long syncedTxid;

    private String checkpointFile = "";

    private volatile boolean finishedRecover = false;

    public FSNameSystem() {
        this.directory = new FSDirectory();
        recoverNamespace();
    }

    /**
     * 创建目录
     * @param path 目录路径
     * @return 是否成功
     */
    public Boolean mkdir(long txid, String path) throws Exception {
        this.directory.mkdir(txid, path);
        return true;
    }

    /**
     * 创建文件
     * @param filename 文件名，包含所在的绝对路径，/products/img001.jpg
     * @return
     * @throws Exception
     */
    public Boolean create(String filename) throws Exception {
        if (!directory.create(filename)) {
            return false;
        }
        return true;
    }

    /**
     * 获取文件目录树的json
     * @return
     * @throws Exception
     */
    public FSImage getFSImage() throws Exception {
        return this.directory.getFSImage();
    }

    /**
     * 获取当前同步到的最大的txid
     * @return
     */
    public long getSyncedTxid() {
        return this.directory.getFSImage().getMaxTxid();
    }

    /**
     * 恢复元数据
     */
    public void recoverNamespace() {
        try {
            loadCheckpointInfo();
            loadFSImage();
            finishedRecover = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 加载fsImage文件到内存里进行恢复
     */
    private void loadFSImage() throws Exception {
        FileInputStream in = null;
        FileChannel channel = null;
        try {
            String path = "D:\\java\\儒猿课堂\\儒猿海量小文件分布式存储项目\\backupnode\\fsImage-" + syncedTxid + ".meta";
            File file = new File(path);
            if (!file.exists()) {
                log.warn("fsimage文件当前不存在，不进行恢复......");
                return;
            }

            in = new FileInputStream(path);
            channel = in.getChannel();

            ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024); // 这个参数是可以动态调节的
            // 每次接收到一个fsimage文件的时候记录一下它的大小，持久化到磁盘上
            // 每次重启就分配对应的空间大小就可以了
            int count = channel.read(buffer);
            buffer.flip();
            String fsImageJson = new String(buffer.array(), 0, count);

            log.info("恢复fsImage文件中的数据：{}", fsImageJson);

            FSDirectory.INode dirTree = JSONObject.parseObject(fsImageJson, FSDirectory.INode.class);
            this.directory.setDirTree(dirTree);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (channel != null) {
                    channel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void loadCheckpointInfo() throws Exception {
        FileInputStream in = null;
        FileChannel channel = null;
        try {
            String path = "D:\\java\\儒猿课堂\\儒猿海量小文件分布式存储项目\\backupnode\\checkpoint-info.meta";
            File file = new File(path);
            if (!file.exists()) {
                log.warn("checkpointInfo文件当前不存在，不进行恢复......");
                return;
            }

            in = new FileInputStream(path);
            channel = in.getChannel();

            ByteBuffer buffer = ByteBuffer.allocate(1024); // 这个参数是可以动态调节的
            // 每次接收到一个fsimage文件的时候记录一下它的大小，持久化到磁盘上
            // 每次重启就分配对应的空间大小就可以了
            int count = channel.read(buffer);
            buffer.flip();

            String checkpointInfo = new String(buffer.array(), 0, count);
            long checkpointTime = Long.valueOf(checkpointInfo.split("_")[0]);
            long syncedTxid = Long.valueOf(checkpointInfo.split("_")[1]);
            String fsimageFile = checkpointInfo.split("_")[2];

            log.info("恢复checkpointTime：{}, syncedTxid: {}, fsimageFile: {}", checkpointTime, syncedTxid, fsimageFile);

            this.checkpointTime = checkpointTime;
            this.syncedTxid = syncedTxid;
            this.checkpointFile = fsimageFile;
            directory.setMaxTxid(syncedTxid);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (channel != null) {
                    channel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public FSDirectory getDirectory() {
        return directory;
    }

    public void setDirectory(FSDirectory directory) {
        this.directory = directory;
    }

    public long getCheckpointTime() {
        return checkpointTime;
    }

    public void setCheckpointTime(long checkpointTime) {
        this.checkpointTime = checkpointTime;
    }

    public void setSyncedTxid(long syncedTxid) {
        this.syncedTxid = syncedTxid;
    }

    public boolean isFinishedRecover() {
        return finishedRecover;
    }

    public void setFinishedRecover(boolean finishedRecover) {
        this.finishedRecover = finishedRecover;
    }

    public String getCheckpointFile() {
        return checkpointFile;
    }

    public void setCheckpointFile(String checkpointFile) {
        this.checkpointFile = checkpointFile;
    }
}
